﻿using ComLib.ComType;
using LogLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace CoreBroker
{
    public class Taker
    {
        private const int PhotosCount = 2;
        /// <summary>
        /// 取消令牌，用于指示拍照任务是否被取消
        /// </summary>
        private bool _bCancelToken = false;
        public bool CancelToken
        {
            get { return _bCancelToken; }
            set { _bCancelToken = value; }
        }

        private string _error;
        public string ErrorMessage
        {
            get { return _error; }
            set { _error = value; }
        }

        private string _fileName = "";

        /// <summary>
        /// 请求参数中需要执行的检测方法
        /// </summary>
        private DetectProcessMethods _method = DetectProcessMethods.DetectAndTake;
        public DetectProcessMethods Method
        {
            get { return _method; }
            set { _method = value; }
        }

        /// <summary>
        /// 镜头视频流播放器
        /// </summary>
        private CapturerBroker _capturer = null;
        public CapturerBroker Capturer
        {
            get
            {
                return _capturer;
            }
            set
            {
                _capturer = value;
            }
        }

        /// <summary>
        /// 照片检测结果报告
        /// </summary>
        protected event TakingCompletedEventHandler TakingCompleted;
        protected void OnTakingCompletedReported(TakingCompletedEventArgs e)
        {
            if (TakingCompleted != null)
            {
                TakingCompleted(this, e);
            }
        }

        public void AddTakingCompletedEvent(TakingCompletedEventHandler handler)
        {
            if (TakingCompleted == null)
            {
                TakingCompleted += handler;
            }
        }

        /// <summary>
        /// 当准备任务被终止时通知界面执行的操作
        /// </summary>
        protected event TakingAbortedEventHandler TaskAborted = null;

        private void OnTaskAborted(int code, string reason)
        {
            if (TaskAborted != null)
            {
                TaskAborted(this, new TakingAbortedEventArgs(code, reason));
            }
        }

        public void AddTaskAbortedEvent(TakingAbortedEventHandler handler)
        {
            if (TaskAborted == null)
            {
                TaskAborted += handler;
            }
        }

        /// <summary>
        /// 照片制证用途
        /// </summary>
        private ApplyTo _apply = ApplyTo.ExitAndEntry;
        public ApplyTo App
        {
            get { return _apply; }
            set { _apply = value; }
        }

        /// <summary>
        /// 等待处理事件集合
        /// </summary>
        private ManualResetEventSlim _mres = new ManualResetEventSlim(false);

        /// <summary>
        /// 执行拍照任务的集合
        /// </summary>
        private Task[] tasks = null;

        private List<TakingResult> listResults = new List<TakingResult>();

        public Taker(CapturerBroker capturer)
        {
            _capturer = capturer;
        }
  
        public void InitData()
        {
            // 添加事件监控       
            _capturer.PhotoCaptured += this.OnPhotoCaptured;        
            _fileName = DateTime.Now.ToString("yyyyMMddHHmmss");
        }

        public void ClearData()
        {
            if (_capturer.InvokeRequired)
            {
                Action outdelegate = new Action(ClearData);
                _capturer.BeginInvoke(outdelegate, new object[] { });
                return;
            }          
            //移除所有事件监控
            _capturer.RemoveAllEvents();
        }

        public void TakeOne(int index)
        {
            if (index < PhotosCount && index < tasks.Length)
            {
                _mres.Reset();
                tasks[index].Start();             
            }
            else
            {
                throw new Exception("参数错误!");
            }
        }

        /// <summary>
        ///  拍照
        /// </summary>
        private void TakePhoto()
        {
            _capturer.TakeFormalPhoto();
        }

        /// <summary>
        /// 开始执行拍照任务
        /// </summary>
        public void StartWork()
        {
            Log.AddBeginningBoundary();
            InitData();
            tasks = new Task[PhotosCount];        
            for (int i = 0; i < PhotosCount; i++)
            {
                int index = i;
                tasks[i] = new Task(() => OnWorkItem(index));
            }
        }

        public void StopWork()
        {
            ClearData();
            Log.AddEndingBoundary();
        }

        /// <summary>
        /// 中断拍照流程
        /// </summary>
        /// <param name="bNormal">拍照业务是否完成</param>
        public void AbortWork()
        {         
            ClearData();
            Log.WriteAsync("拍照已中断");
        }

        private void OnWorkItem(int index)
        {
            string strLog = $"第{index}张拍照任务开始...";
            Log.WriteAsync(strLog);
            // 引发拍照事件
            TakePhoto();
            // 等待拍照完成
            if(_mres.Wait(3000))
            {
                Log.WriteAsync($"第{index}张拍摄完成");
            }
            else
            {
                Log.WriteAsync($"未等到第{index}张拍摄完成");
            }
            // 获得信号, 准备请求数据
            DetectResultArgs result = null;
            if (OnRequesting(index, out result))
            {
                HandlingDetectResult(result); 
            }
            Log.WriteAsync($"第{index}张拍照任务结束.");
        }

        /// <summary>
        /// 向照片检测服务器请求处理照片，等待返回结果
        /// </summary>
        /// <returns></returns>
        private bool OnRequesting(int index, out DetectResultArgs result)
        {
            DetectRequestArgs RequestArgs = new DetectRequestArgs();
            RequestArgs.App = _apply;
            RequestArgs.Index = index;
            RequestArgs.Method = _method;
            RequestArgs.PhotoName = _fileName;
            string jsonRequestString = JsonConvert.SerializeObject(RequestArgs);
            Log.WriteAsync("发送请求:" + jsonRequestString);
            PhotoDetectClient client = new PhotoDetectClient("test_pipe", ".");
            string reponseString = client.Request(jsonRequestString);
            if (string.IsNullOrEmpty(reponseString))
            {
                Log.WriteAsync("Server返回结果为空");
                result = new DetectResultArgs();
                result.ErrorMessage = "Server返回结果为空";
                return false;
            }
            Log.WriteAsync("收到服务器返回信息:" + reponseString);
            result = JsonConvert.DeserializeObject<DetectResultArgs>(reponseString);
            if (!result.Success)
            {
                Log.WriteAsync("检测执行失败:" + result.ErrorMessage);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 处理服务器返回的检测结果;
        /// 检查相关检测项是否合格，由此决定下一步执行的检测方法
        /// </summary>
        private void HandlingDetectResult(DetectResultArgs result)
        {
            RecogOut detectRes = result.Result;
            PassElements elem = detectRes.PassElems;           
            // 首先检查正脸检测是否通过，因为这是最基础的检测项
            if (!elem.FaceEye)
            {
                string logInfo = string.Format("第{0}张照片结果: 正脸眼睛检测不通过, {1}", result.Index, detectRes.Correction.Tips);
                Log.WriteAsync(logInfo);
            }         
            // 当像素综合检测无法通过时
            else if (!elem.PxlQcPass)
            {
                string logInfo = string.Format("第{0}张照片结果: PXL综合检测不通过, {1}", result.Index, detectRes.Correction.Tips);
                Log.WriteAsync(logInfo);
            }
            // 当检测通过时
            else if (detectRes.PassElems.DetectPassed)
            {
                string logInfo = string.Format("第{0}张照片结果: 综合检测通过。", result.Index);
                Log.WriteAsync(logInfo);
            }
            SaveResult(result);
        }

        /// <summary>
        /// 将检测结果报告给UI线程处理
        /// </summary>
        /// <param name="detectRes"></param>
        private void SaveResult(DetectResultArgs args)
        {
            try
            {
                Log.WriteAsync("SaveResult in");
                RecogOut detectRes = args.Result;
                TakenPhoto item = new TakenPhoto(
                    args.Index,
                    detectRes.SourceImagePath,
                    detectRes.DestImagePath,
                    detectRes.PassElems.QcDllCheck);
                TakingResult result = new TakingResult();
                result.DetectResult = detectRes;
                if (args.Success)
                {
                    result.ErrorMessage = detectRes.Correction.Tips;
                }
                else
                {
                    result.ErrorMessage = args.ErrorMessage;
                }
                item.NotPassReason = result.ErrorMessage;
                result.Success = detectRes.PassElems.DetectPassed;
                result.Photo = item;
                Log.WriteAsync("SaveResult in,photo：" + detectRes.DestImagePath);
                listResults.Add(result);
            }
            catch (Exception ex)
            {
                Log.WriteExceptionAsync("SaveResult 异常:"+ex.ToString());
            }                    
        }

        /// <summary>
        /// 将检测结果报告给UI线程处理
        /// </summary>
        /// <param name="detectRes"></param>
        private void ReportResultToUI()
        {
            Log.WriteAsync("所有拍照任务已完成，向主界面报告检测结果");
            TakingCompletedEventArgs e = new TakingCompletedEventArgs(listResults);
            OnTakingCompletedReported(e);
        }

        /// <summary>
        /// 所有检测项通过, 结束准备流程，进入下一步正式拍照
        /// </summary>
        /// <param name="detectRes"></param>
        public void WaitForTasksCompleted()
        {
            Task.WaitAll(tasks);
            ReportResultToUI();
            _mres.Dispose();
            StopWork();
        }

        public async Task WaitForTasksCompletedAsync()
        {
            await Task.Run(WaitForTasksCompleted);           
        }

        private void OnPhotoCaptured(object sender, EventArgs e)
        {
            // 使等待的后台线程获得信号，继续执行后台工作
            _mres.Set();
        }
    }
}
